/***************************************************************************
 *   Copyright (C) 2009 by maarten,,,   *
 *   maarten@maarten-laptop   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "parser.h"
#include <iostream>
#include <QString>
#include "scenemanager/scenemanager.h"
#include "scenemanager/camera/camera.h"
#include "scenemanager/scene/scene.h"
#include "scenemanager/light/pointlight.h"
#include "scenemanager/geometry/cylinder.h"
#include "scenemanager/geometry/geometry.h"
#include "scenemanager/geometry/cone.h"
#include "objects/objectlist.h"
#include "objects/vertex.h"
#include <qmessagebox.h>

#include "scenemanager/scene/subscene.h"
#include "scenemanager/scene/shape.h"

using namespace std;

namespace parser {

Parser::Parser(scene::SceneManager * sceneMgr)
{
    m_sceneMgr = sceneMgr;
    m_status = -1;
    m_sceneTreeLevel = 0;
    m_currentSubScene = NULL;
    
}

Parser::~Parser()
{
}

bool Parser::startDocument()
{
    return TRUE;
}

bool Parser::startElement( const QString&, const QString&, 
                                        const QString& qName, 
                                        const QXmlAttributes& attributes)
{
    if(m_status == -1){
      bool ret = parseOther(qName, attributes);
      if(!ret)
        m_sceneMgr->setStatus(-1);
      return ret;
    }
    
    if(m_status == 3){
      bool ret = parseGeometry(qName, attributes);
      if(!ret)
        m_sceneMgr->setStatus(-1);
      return ret;
    }
    
    if(m_status == 5){
      bool ret =  parseMaterial(qName, attributes);
      if(!ret)
        m_sceneMgr->setStatus(-1);
      return ret;
    }
    if(m_status == 4){
      bool ret =  parseTexture(qName, attributes);
      if(!ret)
        m_sceneMgr->setStatus(-1);
      return ret;
    }
    
    if(m_status == 6){
      bool ret = parseSubScenes(qName, attributes);
      if(!ret)
        m_sceneMgr->setStatus(-1);
      return ret;
    }

    return TRUE;
}

bool Parser::parseSubScenes(const QString& qName, const QXmlAttributes&
                                                                  attributes)
{
  if(m_sceneTreeLevel ==0)
  {
      if(qName == "Shape"){
        cout << "Adding shape to base scene...\n";
        m_sceneMgr->getScene()->addSubScene(parseShape(attributes));
      }
      else if(qName == "Translate"){
        cout << "Adding translation to base scene...\n";
        m_currentSubScene = new scene::SubScene(scene::OPERATION_TYPE(1));
        Vertex *newVertex = parseTuple3(attributes.value("vector"));
        m_currentSubScene->setTransFormation(newVertex);
        m_sceneMgr->getScene()->addSubScene(m_currentSubScene);
        m_sceneTreeLevel++;
      }
      else if(qName == "Rotate"){
        cout << "Adding rotation to base scene...\n";
        m_currentSubScene = new scene::SubScene(scene::OPERATION_TYPE(2));
        Vertex *newVertex = parseTuple3(attributes.value("axis"));
        float angle = attributes.value("angle").toFloat();
        m_currentSubScene->setTransFormation(newVertex,angle);
        m_sceneMgr->getScene()->addSubScene(m_currentSubScene);
        m_sceneTreeLevel++;
      }
      else if(qName == "Scale"){
        cout << "Adding scale to base scene...\n";
        m_currentSubScene = new scene::SubScene(scene::OPERATION_TYPE(3));
        Vertex *newVertex = parseTuple3(attributes.value("scale"));
        m_currentSubScene->setTransFormation(newVertex);
        m_sceneMgr->getScene()->addSubScene(m_currentSubScene);
        m_sceneTreeLevel++;
        cout << "Added scale to base scene...\n";
      }
  }
  else{
      if(qName == "Shape"){
        cout << "Adding shape to scene " << m_sceneTreeLevel << "...\n";
        m_currentSubScene->addSubScene(parseShape(attributes));
      }
      else if(qName == "Translate"){
        cout << "Adding translation to scene "<< m_sceneTreeLevel <<"...\n";
        scene::SubScene *temp = new
                  scene::SubScene(scene::OPERATION_TYPE(1),m_currentSubScene);
        Vertex *newVertex = parseTuple3(attributes.value("vector"));
        cout << "scale:";
        newVertex->print();
        cout << endl;
        temp->setTransFormation(newVertex);
        m_currentSubScene->addSubScene(temp);
        m_currentSubScene = temp;
        m_sceneTreeLevel++;
      }
      else if(qName == "Rotate"){
        cout << "Adding rotate to scene "<< m_sceneTreeLevel <<"...\n";
        scene::SubScene *temp = new
                  scene::SubScene(scene::OPERATION_TYPE(2),m_currentSubScene);
        Vertex *newVertex = parseTuple3(attributes.value("axis"));
        float angle = attributes.value("angle").toFloat();
        temp->setTransFormation(newVertex,angle);
        m_currentSubScene->addSubScene(temp);
        m_currentSubScene = temp;
        m_sceneTreeLevel++;
      }
      else if(qName == "Scale"){
        cout << "Adding scaling to scene "<< m_sceneTreeLevel <<"...\n";
        scene::SubScene *temp = new
                  scene::SubScene(scene::OPERATION_TYPE(3),m_currentSubScene);
        Vertex *newVertex = parseTuple3(attributes.value("scale"));
        cout << "scale:";
        newVertex->print();
        cout << endl;
        temp->setTransFormation(newVertex);
        m_currentSubScene->addSubScene(temp);
        m_currentSubScene = temp;
        m_sceneTreeLevel++;
        
      }
  }
}

scene::Shape* Parser::parseShape(const QXmlAttributes& attributes)
{
  scene::Shape * newShape = new scene::Shape();
  scene::Geometry *geo = NULL;
  scene::Material *mat = NULL;
  scene::Texture  *tex = NULL;
  QString geoString = "" + attributes.value("geometry");
  QString matString = "" + attributes.value("material");
  QString texString = "" + attributes.value("texture");
  
  geo = (scene::Geometry*)m_sceneMgr->getGeometry(geoString);
  
  if(matString == ""){
      cout << "EMPTY MATERIAL\n";
      //cin.get();
  }else{
    mat =  (scene::Material*)m_sceneMgr->getMaterial(matString);
  }
    
  if(texString == ""){
      cout << "EMPTY TEXTURE\n";
      //cin.get();
  }else{
    cout << "TEXTURE FOUND\n";
    tex = (scene::Texture*)m_sceneMgr->getTexture(texString);
  }
  
  newShape->setGeometry(geo);
  newShape->setMaterial(mat);
  newShape->setTexture(tex);
  
  return newShape;
  
}

bool Parser::parseOther(const QString& qName, const QXmlAttributes& attributes)
{
        if(qName == "Camera"){
            parseCam(attributes.value("name"), attributes.value("position"),
            attributes.value("direction"), attributes.value("up"),
            attributes.value("fovy"));
        }
        else if(qName == "PointLight"){
            parseLight(attributes.value("name"),
    attributes.value("position"),
            attributes.value("intensity"),attributes.value("color"));
        }
        else if(qName == "SurfaceLight"){
            parseSurfaceLight(attributes.value("name"),
    attributes.value("position"),
            attributes.value("intensity"),attributes.value("color"),
attributes.value("alpha"), attributes.value("beta"),
attributes.value("surfaceFactor" ));
        }
        else if(qName == "Geometry"){
            m_status = 3; // set status to geometry for next
        }
        else  if(qName == "Scene"){
            cout << "CHANGING STATE TO 6\n";
            m_status = 6;
            parseScene(attributes.value("camera"),
              attributes.value("lights"),attributes.value("background"),
              attributes.value("antiAliasing"));
        }  
        else  if(qName == "Textures"){
            cout << "CHANGING STATE TO 4\n";
            m_status = 4;
        } 
        else  if(qName == "Materials"){
            cout << "CHANGING STATE TO 5\n";
            m_status = 5;
        } 
    return true;
 }

bool Parser::endElement( const QString&, const QString&, const QString& qName)
{
  if(qName == "Translate" || 
     qName == "Scale" ||
     qName == "Rotate"){
      
      m_sceneTreeLevel--;
      cout << "Treelevel going step down: now at "<< m_sceneTreeLevel<< "...\n";
      m_currentSubScene = m_currentSubScene->getParent();
    
  }
    return TRUE;
}

bool Parser::parseCam(QString name, QString pos, QString dir,QString up,
QString fovy)
{
    scene::Camera *newCam = new scene::Camera();
    newCam->setPosition(parseTuple3(pos));
    Vertex * camDir = parseTuple3(dir);
    camDir->setH(0);
    newCam->setDirection(camDir);
    Vertex * upDir = parseTuple3(up);
    upDir->setH(0);
    newCam->setUp(upDir);
    newCam->setFovy(fovy.toFloat());
    newCam->setName(name);

    m_sceneMgr->addCamera(newCam);
    return true;
}

bool Parser::parseLight(QString name, QString pos, QString
intensity,QString color)
{
    scene::PointLight * newLight = new scene::PointLight();
    newLight->setType("light");
    newLight->setName(name);
    newLight->setColor(parseTuple3(color));
    newLight->setPos(parseTuple3(pos));
    newLight->setIntensity(intensity.toFloat());

    m_sceneMgr->addLight(newLight);
    return true;
}

bool Parser::parseSurfaceLight(QString name, QString pos, QString
intensity,QString color, QString alpha, QString beta, QString surfFactor)
{
    m_sceneMgr->addSurfaceLight(name);
    int rows = surfFactor.toInt();
    int cols = surfFactor.toInt();
    
    Vertex * vAlpha = parseTuple3(alpha);
    Vertex * vBeta = parseTuple3(beta);
    Vertex * vPos = parseTuple3(pos);
    
    float rowX = (vPos->x() - vAlpha->x())/(float)rows;
    float rowY = (vPos->y() - vAlpha->y())/(float)rows;
    float rowZ = (vPos->z() - vAlpha->z())/(float)rows;
    
    float colX = (vPos->x() - vBeta->x())/(float)cols;
    float colY = (vPos->y() - vBeta->y())/(float)cols;
    float colZ = (vPos->z() - vBeta->z())/(float)cols;
    
    for(int i = 0; i < rows; ++i){
      for(int j = 0; j < cols; ++j){
        Vertex * vColor = parseTuple3(color);
        
        Vertex * newPos = new Vertex(0, 0, 0);
              
        scene::PointLight * newLight = new scene::PointLight();
        newLight->setColor(vColor);
        newLight->setIntensity(intensity.toFloat());
        newLight->setPos(newPos);
        newLight->setType("light");
        QString newName = name;
        newName.append(QString::number(i));
        newName.append(QString::number(j));
        newLight->setName(newName);
        newLight->setAlpha(vAlpha);
        newLight->setBeta(vBeta);
        newLight->setSurfacePos(vPos);
        
        newLight->setrowX(rowX);
        newLight->setrowY(rowY);
        newLight->setrowZ(rowZ);
        newLight->setcolX(colX);
        newLight->setcolY(colY);
        newLight->setcolZ(colZ);
        newLight->setSurfaceFactor(rows);
        m_sceneMgr->addLight(newLight);
        cout << newLight->name().toStdString() << endl;
      }
    }
    
    return true;
}

bool Parser::parseCylinder(QString name, QString radius, QString height,
                            QString capped)
{
    scene::Cylinder * newCylinder = new scene::Cylinder();
    newCylinder->setName(name);
    newCylinder->setHeight(height.toFloat());
    newCylinder->setType("geometry");
    newCylinder->setRadius(radius.toFloat());
    newCylinder->setCapped(parseBool(capped));

    m_sceneMgr->addSceneElement(newCylinder);
    return true;
}

bool Parser::parseMaterial(const QString& qName, const QXmlAttributes&
                                                                  attributes)
{
  /*********************************************************************
  * First check if status should be changed
  **********************************************************************/

  if(qName == "Scene"){
    cout << "CHANGING STATE TO 6\n";
    m_status = 6;
    parseScene(attributes.value("camera"), attributes.value("lights"),
                              attributes.value("background"),
attributes.value("antiAliasing"));
    return true;
  }  
  
  /*********************************************************************
  * A material object will be parsed
  **********************************************************************/
  
  if(qName == "DiffuseMaterial")
  {
      Vertex *color = parseTuple3(attributes.value("color"));
      int reflect = attributes.value("reflect").toInt();
      int refract = attributes.value("refract").toInt();
      
      
        
      //cout << "diffuse" << reflect << ", " << refract << endl;
      scene::Material * mat = new scene::Material(color,0);
      
      if(attributes.value("glossy").compare("") != 0){
        mat->setGlossyFactor(attributes.value("glossy").toFloat());
        cout << "Glossy set.." << endl;
      }
      else{
        cout << "no glossy set..." << endl;
      }
      
      mat->setName(attributes.value("name"));
      if(reflect == 1){
        mat->setReflect(true);
        Vertex * specColor = parseTuple3(attributes.value("specColor"));
        mat->setSpecularColor(specColor);
      }
      else if(refract == 1)
        mat->setRefract(true);
      
      m_sceneMgr->addMaterial(mat);
  }
  else if(qName == "PhongMaterial")
  {
      Vertex *color = parseTuple3(attributes.value("color"));
      scene::Material * mat = new scene::Material(color,1);
      mat->setName(attributes.value("name"));
      int shinyness = attributes.value("shininess").toInt();
      mat->setShinyness(shinyness);
      int reflect = attributes.value("reflect").toInt();
      int refract = attributes.value("refract").toInt();
      if(reflect == 1){
        mat->setReflect(true);
        Vertex * specColor = parseTuple3(attributes.value("specColor"));
        mat->setSpecularColor(specColor);
      }
      else if(refract == 1)
        mat->setRefract(true);
      
      if(attributes.value("glossy").compare("") != 0){
        mat->setGlossyFactor(attributes.value("glossy").toFloat());
        cout << "Glossy set.." << endl;
      }
      else{
        cout << "no glossy set..." << endl;
      }
      m_sceneMgr->addMaterial(mat);
  }
  else if(qName == "LinearCombinedMaterial")
  {
      scene::Material * mat = new scene::Material(2);
      mat->setName(attributes.value("name"));
      scene::Material * mat1 = (scene::Material*)
m_sceneMgr->getMaterial(attributes.value("material1"));
      scene::Material * mat2 = (scene::Material*)
m_sceneMgr->getMaterial(attributes.value("material2"));

      if(mat1== NULL || mat2==NULL){
        cout << "WRONG COMBINED MATERIAL" << endl;
        return false;
      }
      
      float weight1 = attributes.value("weight1").toFloat();
      float weight2 = attributes.value("weight2").toFloat();
      
      if(attributes.value("glossy").compare("") != 0){
        mat->setGlossyFactor(attributes.value("glossy").toFloat());
        cout << "Glossy set.." << endl;
      }
      else{
        cout << "no glossy set..." << endl;
      }
      
      mat->setMat1(mat1);
      mat->setMat2(mat2);
      mat->setWeight1(weight1);
      mat->setWeight2(weight2);

      int reflect = attributes.value("reflect").toInt();
      int refract = attributes.value("refract").toInt();
      if(reflect == 1){
        mat->setReflect(true);
        Vertex * specColor = parseTuple3(attributes.value("specColor"));
        mat->setSpecularColor(specColor);
      }
      else if(refract == 1)
        mat->setRefract(true);
      m_sceneMgr->addMaterial(mat);
  }else{
    return false;
  }
  
  
}

bool Parser::parseTexture(const QString& qName, const QXmlAttributes&
                                                                  attributes)
{
  /*********************************************************************
  * First check if status should be changed
  **********************************************************************/
  if(qName == "Materials"){
    m_status = 5;
    return true;
  }
  if(qName == "Scene"){
    cout << "CHANGING STATE TO 6\n";
    m_status = 6;
    parseScene(attributes.value("camera"), attributes.value("lights"),
            attributes.value("background"),attributes.value("antiAliasing"));
    return true;
  }  
  
  /*********************************************************************
  * A material object will be parsed
  **********************************************************************/
  
  if(qName == "Texture")
  {
      QString textfile = attributes.value("src");
      scene::Texture * text = new scene::Texture(textfile);
      text->setName(attributes.value("name"));
      text->setRepeat(attributes.value("repeat").toInt());
      m_sceneMgr->addTexture(text);
  }
  else{
    return false;
  }
  
  
}

bool Parser::parseGeometry(const QString& qName, const QXmlAttributes&
                                                                  attributes)
{
  /*********************************************************************
  * First check if status should be changed
  **********************************************************************/
  if(qName == "Textures"){
    m_status = 4;
    return true;
  }
  if(qName == "Materials"){
    m_status = 5;
    return true;
  }
  if(qName == "Scene"){
    cout << "CHANGING STATE TO 6\n";
    m_status = 6;
    parseScene(attributes.value("camera"), attributes.value("lights"),
        attributes.value("background"),attributes.value("antiAliasing"));
    return true;
  }  
  
  /*********************************************************************
  * A geometry object will be parsed
  **********************************************************************/
  
  // first check if object exists in database
  bool objectFound =true;
  int objectId = -1;
  
  
  // LOAD WHEN USED, NOT HER FIXME
  if( (objectFound = m_sceneMgr->loadObject("assets/objects/" +qName.toLower() +
".obj"))){
    objectId = m_sceneMgr->getObjectList()->size()-1;
  }
  else{
    qDebug("No such object found in database");
    return false;
  }
  
  if(objectId == -1){ // parse specific preprogrammed object
    // TODO
    return false;
  }
  
  // parse object from datafile
  scene::Geometry *newGeo = new scene::Geometry(objectId);
  newGeo->setName(attributes.value("name"));
  
  m_sceneMgr->addGeometry(newGeo);
    
  return objectFound;

}

bool Parser::parseCone(QString name, QString radius, QString height,
                            QString capped)
{
    scene::Cone * newCone = new scene::Cone();
    newCone->setName(name);
    newCone->setHeight(height.toFloat());
    newCone->setType("geometry");
    newCone->setRadius(radius.toFloat());
    newCone->setCapped(parseBool(capped));

    m_sceneMgr->addSceneElement(newCone);
    return true;
}

bool Parser::parseScene(QString camString, QString lightsString, QString
colorString, QString antiAliasing)
{
    
    QStringList lightsList = lightsString.split(", ");

    object::Vertex *colorVector = parseTuple3(colorString);
    QColor bgColor;
    bgColor.setRgbF(colorVector->x(),colorVector->y(),colorVector->z());

    scene::Camera *cam = m_sceneMgr->getCamera(camString);    
    
    if(cam == NULL || lightsList.size() == 0){
        qDebug("Parsing scene failed...:");
        qDebug("Cam not available or no lights given ");
        return false;
    }
    
    float aa = 1;
    
    if(antiAliasing.compare("") !=0)
      aa = antiAliasing.toInt();
    
    QVector<scene::SceneElement*> *lights = new QVector<scene::SceneElement*>;
    
    for(int i = 0; i< lightsList.size(); ++i){
      if(m_sceneMgr->getLight(QString(lightsList[i]))==NULL){
        // maybe it is a surfaceLight
        if(m_sceneMgr->isSurfaceLight(QString(lightsList[i]))){
          //generate lightsList
          int rows = 1;
          int cols = 1;
          for(int r = 0; r < rows; ++r)
            for(int s = 0; s < cols; ++s){
              QString newName = QString(lightsList[i]);
              newName.append(QString::number(r));
              newName.append(QString::number(s));
              if(m_sceneMgr->getLight(newName)==NULL){
                cout << "SOMETHING WENT TERRIBLY WRONG IN SURFACELIGHTS\n";
              }
              else{
               rows = 
((scene::PointLight*)(m_sceneMgr->getLight(newName)))->surfaceFactor();
              cols = rows;
                lights->push_back(m_sceneMgr->getLight(newName));
                //cout << "surfacelight nicely parsed\n";
              }
            }
            
            ;
          
        }
        else{
          qDebug("Lightnames do not match defined lights");
          return false;
        }
      }
      else{
        lights->push_back( m_sceneMgr->getLight(QString(lightsList[i])));
      }
    }

    cout << "ANTIALIASING:" << aa << endl;
    m_sceneMgr->addScene(new scene::Scene(cam, lights, bgColor, aa));
    cout << "-->SCENE ADDED\n";

    return true;
}

///////////////////////////////////////////////////////////////////
// help functions
///////////////////////////////////////////////////////////////////

object::Vertex* Parser::parseTuple3(QString tuple)
{
    QStringList list = tuple.split(" ");
    return new object::Vertex(list.at(0).toFloat(), list.at(1).toFloat(),
list.at(2).toFloat());
}

bool Parser::parseBool(QString boolString)
{
    if(boolString.compare("true") == 0){
        return true;
    }else{
        return false;
    }
}


} // end namespace parser
